package com.mask.token.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.mask.token.model.TokenInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis Token存储服务
 * 使用Redis存储Token信息，支持分布式和持久化
 *
 * @author mask
 * @since 1.0.0
 */
@Slf4j
@Service
@ConditionalOnClass(RedisTemplate.class)
public class MaskTokenRedisService {

    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    public MaskTokenRedisService(RedisTemplate<String, String> redisTemplate, ObjectMapper objectMapper) {
        this.redisTemplate = redisTemplate;
        this.objectMapper = objectMapper;
        // 确保ObjectMapper支持JavaTime模块
        this.objectMapper.registerModule(new JavaTimeModule());
    }

    /**
     * Redis Key前缀
     */
    private static final String TOKEN_PREFIX = "mask:token:";
    private static final String USER_TOKENS_PREFIX = "mask:user:tokens:";
    private static final String USER_DEVICE_PREFIX = "mask:user:device:";

    /**
     * 存储Token信息
     *
     * @param tokenInfo Token信息
     * @param expireTime 过期时间
     */
    public void storeToken(TokenInfo tokenInfo, Duration expireTime) {
        try {
            String tokenKey = TOKEN_PREFIX + tokenInfo.getToken();
            String tokenJson = objectMapper.writeValueAsString(tokenInfo);
            
            // 存储Token信息
            redisTemplate.opsForValue().set(tokenKey, tokenJson, expireTime);
            
            // 存储用户Token列表
            String userTokensKey = USER_TOKENS_PREFIX + tokenInfo.getUserId();
            redisTemplate.opsForSet().add(userTokensKey, tokenInfo.getToken());
            redisTemplate.expire(userTokensKey, expireTime);
            
            // 存储用户设备信息
            if (tokenInfo.getDeviceId() != null) {
                String userDeviceKey = USER_DEVICE_PREFIX + tokenInfo.getUserId() + ":" + tokenInfo.getDeviceId();
                redisTemplate.opsForValue().set(userDeviceKey, tokenInfo.getToken(), expireTime);
            }
            
            log.debug("Token已存储到Redis: {}", tokenInfo.getToken());
        } catch (JsonProcessingException e) {
            log.error("存储Token到Redis失败", e);
        }
    }

    /**
     * 获取Token信息
     *
     * @param token Token字符串
     * @return Token信息
     */
    public TokenInfo getTokenInfo(String token) {
        try {
            String tokenKey = TOKEN_PREFIX + token;
            String tokenJson = redisTemplate.opsForValue().get(tokenKey);
            
            if (tokenJson != null) {
                TokenInfo tokenInfo = objectMapper.readValue(tokenJson, TokenInfo.class);
                // 更新最后活跃时间
                tokenInfo.updateLastActiveTime();
                long expireSeconds = getTokenExpireTime(token);
                if (expireSeconds > 0) {
                    storeToken(tokenInfo, Duration.ofSeconds(expireSeconds));
                }
                return tokenInfo;
            }
            
            return null;
        } catch (JsonProcessingException e) {
            log.error("从Redis获取Token信息失败", e);
            return null;
        }
    }

    /**
     * 删除Token
     *
     * @param token Token字符串
     */
    public void removeToken(String token) {
        try {
            TokenInfo tokenInfo = getTokenInfo(token);
            if (tokenInfo != null) {
                String tokenKey = TOKEN_PREFIX + token;
                redisTemplate.delete(tokenKey);
                
                // 从用户Token列表中移除
                String userTokensKey = USER_TOKENS_PREFIX + tokenInfo.getUserId();
                redisTemplate.opsForSet().remove(userTokensKey, token);
                
                // 删除用户设备信息
                if (tokenInfo.getDeviceId() != null) {
                    String userDeviceKey = USER_DEVICE_PREFIX + tokenInfo.getUserId() + ":" + tokenInfo.getDeviceId();
                    redisTemplate.delete(userDeviceKey);
                }
                
                log.debug("Token已从Redis删除: {}", token);
            }
        } catch (Exception e) {
            log.error("从Redis删除Token失败", e);
        }
    }

    /**
     * 删除用户所有Token
     *
     * @param userId 用户ID
     */
    public void removeUserAllTokens(Long userId) {
        try {
            String userTokensKey = USER_TOKENS_PREFIX + userId;
            Set<String> tokens = redisTemplate.opsForSet().members(userTokensKey);
            
            if (tokens != null && !tokens.isEmpty()) {
                // 删除所有Token
                for (String token : tokens) {
                    String tokenKey = TOKEN_PREFIX + token;
                    redisTemplate.delete(tokenKey);
                }
                
                // 删除用户Token列表
                redisTemplate.delete(userTokensKey);
                
                // 删除用户设备信息
                String userDevicePattern = USER_DEVICE_PREFIX + userId + ":*";
                Set<String> deviceKeys = redisTemplate.keys(userDevicePattern);
                if (deviceKeys != null && !deviceKeys.isEmpty()) {
                    redisTemplate.delete(deviceKeys);
                }
                
                log.debug("用户 {} 的所有Token已从Redis删除", userId);
            }
        } catch (Exception e) {
            log.error("从Redis删除用户所有Token失败", e);
        }
    }

    /**
     * 获取用户在线设备数量
     *
     * @param userId 用户ID
     * @return 在线设备数量
     */
    public int getUserOnlineDeviceCount(Long userId) {
        try {
            String userTokensKey = USER_TOKENS_PREFIX + userId;
            Set<String> tokens = redisTemplate.opsForSet().members(userTokensKey);
            
            if (tokens == null || tokens.isEmpty()) {
                return 0;
            }
            
            int count = 0;
            for (String token : tokens) {
                if (isTokenValid(token)) {
                    count++;
                }
            }
            
            return count;
        } catch (Exception e) {
            log.error("获取用户在线设备数量失败", e);
            return 0;
        }
    }

    /**
     * 获取用户所有Token信息
     *
     * @param userId 用户ID
     * @return Token信息列表
     */
    public List<TokenInfo> getUserAllTokens(Long userId) {
        try {
            String userTokensKey = USER_TOKENS_PREFIX + userId;
            Set<String> tokens = redisTemplate.opsForSet().members(userTokensKey);
            
            List<TokenInfo> tokenInfos = new ArrayList<>();
            if (tokens != null && !tokens.isEmpty()) {
                for (String token : tokens) {
                    TokenInfo tokenInfo = getTokenInfo(token);
                    if (tokenInfo != null) {
                        tokenInfos.add(tokenInfo);
                    }
                }
            }
            
            return tokenInfos;
        } catch (Exception e) {
            log.error("获取用户所有Token信息失败", e);
            return new ArrayList<>();
        }
    }

    /**
     * 检查Token是否有效
     *
     * @param token Token字符串
     * @return 是否有效
     */
    public boolean isTokenValid(String token) {
        try {
            String tokenKey = TOKEN_PREFIX + token;
            return redisTemplate.hasKey(tokenKey);
        } catch (Exception e) {
            log.error("检查Token有效性失败", e);
            return false;
        }
    }

    /**
     * 延长Token过期时间
     *
     * @param token Token字符串
     * @param expireTime 新的过期时间
     */
    public void extendTokenExpireTime(String token, Duration expireTime) {
        try {
            String tokenKey = TOKEN_PREFIX + token;
            redisTemplate.expire(tokenKey, expireTime);
            
            TokenInfo tokenInfo = getTokenInfo(token);
            if (tokenInfo != null) {
                String userTokensKey = USER_TOKENS_PREFIX + tokenInfo.getUserId();
                redisTemplate.expire(userTokensKey, expireTime);
                
                if (tokenInfo.getDeviceId() != null) {
                    String userDeviceKey = USER_DEVICE_PREFIX + tokenInfo.getUserId() + ":" + tokenInfo.getDeviceId();
                    redisTemplate.expire(userDeviceKey, expireTime);
                }
            }
            
            log.debug("Token过期时间已延长: {}", token);
        } catch (Exception e) {
            log.error("延长Token过期时间失败", e);
        }
    }

    /**
     * 清理过期的Token
     */
    public void cleanExpiredTokens() {
        try {
            String tokenPattern = TOKEN_PREFIX + "*";
            Set<String> tokenKeys = redisTemplate.keys(tokenPattern);
            
            if (tokenKeys != null && !tokenKeys.isEmpty()) {
                int cleanedCount = 0;
                for (String tokenKey : tokenKeys) {
                    if (!redisTemplate.hasKey(tokenKey)) {
                        cleanedCount++;
                    }
                }
                log.info("清理了 {} 个过期Token", cleanedCount);
            }
        } catch (Exception e) {
            log.error("清理过期Token失败", e);
        }
    }

    /**
     * 获取Token剩余过期时间
     *
     * @param token Token字符串
     * @return 剩余过期时间（秒）
     */
    public long getTokenExpireTime(String token) {
        try {
            String tokenKey = TOKEN_PREFIX + token;
            return redisTemplate.getExpire(tokenKey, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("获取Token过期时间失败", e);
            return -1;
        }
    }

    /**
     * 获取Redis连接信息
     *
     * @return Redis连接信息
     */
    public String getRedisInfo() {
        try {
            return redisTemplate.getConnectionFactory().getConnection().info().toString();
        } catch (Exception e) {
            log.error("获取Redis信息失败", e);
            return "Redis连接失败";
        }
    }
}
